# Redux 背后的架构思想——认识 Flux 架构
Redux 的设计在很大程度上受益于 Flux 架构,我们可以认为 Redux 是 Flux 的一种实现形式(虽然它并不严格遵循 Flux 的设定),理解 Flux 将帮助你更好地从抽象层面把握 Redux。
Flux 并不是一个具体的框架,它是一套由 Facebook 技术团队提出的应用架构,这套架构约束的是应用处理数据的模式。在 Flux 架构中,一个应用将被拆分为以下 4 个部分。
- View(视图层):用户界面。该用户界面可以是以任何形式实现出来的,React 组件是一种形式,Vue、Angular 也完全 OK。Flux 架构与 React 之间并不存在耦合关系。
- Action(动作):也可以理解为视图层发出的“消息”,它会触发应用状态的改变。
- Dispatcher(派发器):它负责对 action 进行分发。
- Store(数据层):它是存储应用状态的“仓库”,此外还会定义修改状态的逻辑。store 的变化最终会映射到 view 层上去。
这 4 个部分之间的协作将通过下图所示的工作流规则来完成配合:

一个典型的 Flux 工作流是这样的:
用户与 View 之间产生交互,通过 View 发起一个 Action;Dispatcher 会把这个 Action 派发给 Store,通知 Store 进行相应的状态更新。Store 状态更新完成后,会进一步通知 View 去更新界面。
值得注意的是,图中所有的箭头都是单向的,这也正是 Flux 架构最核心的一个特点——单向数据流。
# Flux 架构到底解决了什么问题
Flux 的核心特征是单向数据流,要想完全了解单向数据流的好处,我们需要先了解双向数据流带来了什么问题
# MVC 模式在前端场景下的局限性
双向数据流最为典型的代表就是前端场景下的 MVC 架构,该架构的示意图如下图所示

除了允许用户通过 View 层交互来触发流程以外,MVC 架构还有另外一种形式,即允许用户通过直接触发 Controller 逻辑来触发流程,这种模式下的架构关系如下图所示:

在 MVC 应用中,会涉及这 3 个部分:
Model(模型),程序需要操作的数据或信息;View(视图),用户界面;Controller(控制器),用于连接View和Model,管理Model与View之间的逻辑
原则上来说,三者的关系应该像上图一样,用户操作 View 后,由 Controller 来处理逻辑(或者直接触发 Controller 的逻辑),经过 Controller 将改变应用到 Model 中,最终再反馈到 View 上。在这个过程中,数据流应该是单向的。
事实上,在许多服务端的 MVC 应用中,数据流确实能够保持单向。但是在前端场景下,实际的 MVC 应用要复杂不少,前端应用/框架往往出于交互的需要,
允许 View 和 Model 直接通信。此时的架构关系就会变成下图这样:

这就允许了双向数据流的存在。当业务复杂度较高时,数据流会变得非常混乱,出现类似下图这种情况:

图中我们的示例只有一个 Controller,但考虑到一个应用中还可能存在多个 Controller,实际的情况应该比上图还要复杂得多
在如此复杂的依赖关系下,再小的项目变更也将伴随着不容小觑的风险——或许一个小小的改动,就会对整个项目造成“蝴蝶效应”般的巨大影响。如此混乱的修改来源,将会使得我们连 Bug 排查都无从下手,因为你很难区分出一个数据的变化到底是由哪个 Controller 或者哪个 View 引发的。
此时再回头看下 Flux 的架构模式,你应该多少能感受到其中的妙处。这里我们再来回顾一下 Flux 中的数据流模式,请看下图:

Flux 最核心的地方在于严格的单向数据流,在单向数据流下,状态的变化是可预测的。如果 store 中的数据发生了变化,那么有且仅有一个原因,那就是由 Dispatcher 派发 Action 来触发的。这样一来,就从根本上避免了混乱的数据关系,使整个流程变得清晰简单
不过这并不意味着 Flux 是完美的。事实上,Flux 对数据流的约束背后是不可忽视的成本:除了开发者的学习成本会提升外,Flux 架构还意味着项目中代码量的增加。
Flux 架构往往在复杂的项目中才会体现出它的优势和必要性。如果项目中的数据关系并不复杂,其实完全轮不到 Flux 登场,这一点对于 Redux 来说也是一样的。
现在你不妨结合 Flux 架构的特性,再去品味一遍 Redux 官方给出的这个定义:
Redux 是 JavaScript 状态容器,它提供可预测的状态管理。
此时的你,想必更加能够体会“可预测”这三个字背后的深意。
# Redux 关键要素与工作流回顾
Redux 主要由 3 部分组成:
Store、Reducer 和 Action。
- Store:它是一个单一的数据源,而且是只读的。
- Action 人如其名,是“动作”的意思,它是对变化的描述。
- Reducer 是一个函数,它负责对变化进行分发和处理,最终将新的数据返回给 Store。
Store、Action 和 Reducer 三者紧密配合,便形成了 Redux 独树一帜的工作流,如下图所示:

在 Redux 的整个工作过程中,数据流是严格单向的。如果你想对数据进行修改,只有一种途径:派发 Action。Action 会被 Reducer 读取,Reducer 将根据 Action 内容的不同执行不同的计算逻辑,最终生成新的 state(状态),这个新的 state 会更新到 Store 对象里,进而驱动视图层面作出对应的改变。
对于组件来说,任何组件都可以以约定的方式从 Store 读取到全局的状态,任何组件也都可以通过合理地派发 Action 来修改全局的状态。Redux 通过提供一个统一的状态容器,使得数据能够自由而有序地在任意组件之间穿梭。
# Redux 是如何工作的
我们先来看一下 Redux 的源码文件夹结构,如下图所示:

其中,utils 是工具方法库;index.js 作为入口文件,用于对功能模块进行收敛和导出。真正“干活”的是功能模块本身,也就是下面这几个文件:
applyMiddleware.js是中间件模块,它的独立性较强bindActionCreators.js用于将传入的actionCreator与dispatch方法相结合,揉成一个新的方法combineReducers.js用于将多个reducer合并起来compose.js用于把接收到的函数从右向左进行组合createStore.jscreateStore方法是我们在使用Redux时最先调用的方法,
